#include "Socket.h"
#include "Log.h"

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstdio>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <string>
#include <iostream>

int create_nonblocking()
{
    // SOCK_NONBLOCK设置SOCKET为非阻塞
    int listenfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
    if (listenfd < 0)
    {
        char buf[256]={0};
        char* ret=strerror_r(errno, buf, sizeof(buf));        
        LOG(ERROR)<<"listen socket create error:"<<ret<<"("<<errno<<")";
        exit(-1);
    }
    return listenfd;
}

Socket::Socket(int fd) : m_fd(fd)
{
}
Socket::~Socket()
{
    ::close(m_fd);
}

int Socket::fd()
{
    return m_fd;
}

void Socket::set_ip_port(const char* ip,uint16_t port)
{
    m_ip=ip;
    m_port=port;
}

void Socket::set_reuseaddr(bool on)
{

    int opt = on ? 1 : 0;
    // SO_REUSEADDR表示重用ip地址，当现有的套接字在 ESTABLISHED 或 TIME-WAIT 状态时，SO_REUSEADDR 选项允许套接字 bind() 到相同的 IP:port 组合
    setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &opt, static_cast<socklen_t>(sizeof opt));
}
void Socket::set_reuseport(bool on)
{
    int opt = on ? 1 : 0;
    // SO_REUSEPORT允许我们将任意数目的socket绑定到完全相同的源地址端口对上
    setsockopt(m_fd, SOL_SOCKET, SO_REUSEPORT, &opt, static_cast<socklen_t>(sizeof opt));
}
void Socket::set_tcpnodelay(bool on)
{
    int opt = on ? 1 : 0;
    // TCP_NODELAY表示禁用Nagle算法。
    setsockopt(m_fd, SOL_SOCKET, TCP_NODELAY, &opt, static_cast<socklen_t>(sizeof opt));
}
void Socket::set_keepalive(bool on)
{
    int opt = on ? 1 : 0;
    // SO_KEEPALIVE 是一个套接字选项，用于设置是否启用 keepalive 机制
    setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE, &opt, static_cast<socklen_t>(sizeof opt));
}
void Socket::bind(const InetAddress &servaddr)
{
    if (::bind(m_fd, servaddr.addr(), sizeof(sockaddr)) < 0)
    {
        char buf[256]={0};
        char* ret=strerror_r(errno, buf, sizeof(buf));        
        LOG(ERROR)<<"bind() failed."<<ret<<"("<<errno<<")";
        close(m_fd);
        exit(-1);
    }
    m_ip = servaddr.ip();
    m_port=servaddr.port();
}
void Socket::listen(int listennum)
{
    if (::listen(m_fd, listennum) != 0)
    {
        char buf[256]={0};
        char* ret=strerror_r(errno, buf, sizeof(buf));        
        LOG(ERROR)<<"listenfd() failed."<<ret<<"("<<errno<<")";
        close(m_fd);
        exit(-1);
    }
}
int Socket::accept(InetAddress &clientaddr)
{
    sockaddr_in peeraddr;
    socklen_t len = sizeof(peeraddr);
    int clientfd = accept4(m_fd, (sockaddr *)&peeraddr, &len, SOCK_NONBLOCK);

    clientaddr.set_addr(peeraddr);
    
    return clientfd;
}

std::string Socket::ip()
{
    return m_ip;
}
uint16_t Socket::port()
{
    return m_port;
}